<?php

namespace App\Services\Payment\Lianlianpay;

use GuzzleHttp\Client;
use Illuminate\Support\Facades\Request;
use Psr\Http\Message\StreamInterface;

class Lianlianpay
{
    private $url = 'https://payserverapi.lianlianpay.com/v1/paycreatebill';

    // 商户私钥
    private $merchant_private_key_path = '\keys\merchant_private_key.pem';

    // 商户公钥
    private $merchant_public_key_path = '\keys\merchant_public_key.pem';

    // 连连公钥
    private $llianpay_public_key_path = '\keys\llianpay_public_key.pem';

    // 商户id
    // private $OID_PARTNER = '2020042200284052';
    private $OID_PARTNER = '402503060000031388';

    // 签名类型
    private $sign_type = 'RSA';

    public function __construct()
    {
        // 商户私钥
        $this->merchant_private_key_path = '\keys\merchant_private_key.pem';

        // 商户公钥
        $this->merchant_public_key_path = '\keys\merchant_public_key.pem';

        // 连连公钥
        $this->llianpay_public_key_path = '\keys\llianpay_public_key.pem';

        // 商户id
        // $this->OID_PARTNER = '2020042200284052';
        $this->oid_partner= env('OID_PARTNER');

        // 签名类型
        $this->sign_type = 'RSA';
    }

    // 线上统一创单
    public function createTrade($order)
    {
         // 构建请求参数
        // $params = new TradeCreateParams();
        $current = date("YmdHis");
        $params = [];
        $params['timestamp'] = $current;
        // $params['oid_partner'] = '2020042200284052';
        $params['oid_partner'] = env('OID_PARTNER');
        $params['txn_type'] = 'GENERAL_CONSUME';
        // $params['user_id'] = 'LLianPayTest-In-User-12345';
        $params['user_id'] = $order['member_id'];
        $params['user_type'] = 'ANONYMOUS';
        $params['notify_url'] = env('ADMIN_ROUTE_DOMAIN') . '/api/payment/llpay_notify';
        // $params['return_url'] = env('ADMIN_ROUTE_DOMAIN') . '/api/payment/llpay_return';

        // $orderInfo = new TradeCreateOrderInfo();
        $orderInfo = [];
        // $orderInfo['txn_seqno'] = $order['order_bn'].'_'.date('YmdHis');
        $orderInfo['txn_seqno'] = $order['order_bn']. rand(1000, 9999);
        $orderInfo['txn_time'] = $current;
        $orderInfo['total_amount'] = $order['amount'];
        $params['orderInfo'] = $orderInfo;

        // $u_payeeInfo = new TradeCreatePayeeInfo();
        $u_payeeInfo = [];
        $u_payeeInfo['payee_id'] = env('OID_PARTNER');
        $u_payeeInfo['payee_type'] = 'MERCHANT';
        $u_payeeInfo['payee_amount'] = $order['amount'];
        $params['payeeInfo'][] = $u_payeeInfo;

        // $m_payeeInfo = new TradeCreatePayeeInfo();
        // $m_payeeInfo = [];
        // $m_payeeInfo['payee_id'] = '2020042200284052';
        // $m_payeeInfo['payee_type'] = 'MERCHANT';
        // $m_payeeInfo['payee_amount'] = 2;
        // $params['payeeInfo'] = array($u_payeeInfo, $m_payeeInfo);

        // 测试环境地址
        // $url = 'https://accpapi-ste.lianlianpay-inc.com/v1/txn/tradecreate';
        // 正式
        $url = 'https://accpapi.lianlianpay.com/v1/txn/tradecreate';
        // dd('sss', $params, $this->merchant_private_key_path);
        $result = self::sendRequest($url, json_encode($params));
        // dd('aa', $result);

        $res = json_decode($result, 1);
        return $res;

        // return $result;
    }

    // 去支付
    public static function toPay($order, $res_)
    {
        $datetime = date('YmdHis');
        $goods_count = array_sum(array_column($order['detail'], 'num'));
        // dd($order, $goods_count);
        $risk_item = [
            'virtual_goods_status' => 0,
            'goods_count' => $goods_count,
            'delivery_full_name' => $order['address']['consignee'],
            'delivery_phone' => $order['address']['mobile'],
            'delivery_addr_province' => $order['address']['province'],
            'delivery_addr_city' => $order['address']['city'],
            'frms_ware_category' => 4016,
            'user_info_mercht_userno' => $order['member_id'],
            'user_info_bind_phone' => '',
            'user_info_dt_register' => date('YmdHis'),
            'goods_name' => $order['detail'][0]['goods_name'],
        ];

        $payerInfo = [
            'payer_type' => 'USER',
            'payer_id' => $order['member_id'],
            // "password" => "9K9+wUL3NuRdqcmtTD7k8PMII8HUb9czISzXp8ff1sPH1jCVxM=",
            // "random_key" => "49d43e84-5d8c-11e8-9c2d-fa7ae01bbebc"
        ];

        $payMethods = [
            [
                'method' => 'WECHAT_APP_WXA',
                // 'method' => 'EBANK_B2B',
                'amount' => $order['amount'],
            ]
        ];

        $extend_params = [
            'req_reserved' => env('WECHAT_MINI_PROGRAM_APPID'),
        ];

        $params = [
            'timestamp' => $datetime,
            'oid_partner' => env('OID_PARTNER'),
            'txn_seqno' => $res_['txn_seqno'],
            'total_amount' => $order['amount'],
            'risk_item' => json_encode($risk_item),
            // 'appid' => env('WECHAT_MINI_PROGRAM_APPID'),
            // 'openid' => 'oh0rz69zWn0bwLm74ACZ33oRNUUc',
            // 'openid' => $order['member']['openid'],
            // 'session_key' => 'sdsdfsdfsdfsdfdsfdsfsdf',
            // 'app_key' => 'ssssssssss',
            'client_ip' => Request::getClientIp(),
            'payerInfo' => $payerInfo,
            'payMethods' => $payMethods,
            'directionalpay_flag' => 'N',
            'extend_params' => json_encode($extend_params),
        ];

        // 测试环境地址
        // $url = 'https://accpapi-ste.lianlianpay-inc.com/v1/txn/payment-gw';
        $url = 'https://accpapi.lianlianpay.com/v1/txn/payment-gw';
        // 正式
        $result = self::sendRequest($url, json_encode($params));
        // dd('aa', $result);

        $res = json_decode($result, 1);
        // dd($res_, $params, $res);
        return $res;
    }

    // 来账宝线下支付
    public static function offlineCreateTrade($order)
    {
        // dd($order);
        $goods_name = array_column($order['detail'], 'goods_name');
        // 构建请求参数
        // $params = new TradeCreateParams();
        $current = date("YmdHis");
        $params = [];
        $params['timestamp'] = $current;
        $params['oid_partner'] = env('OID_PARTNER');
        // $params['match_success_url'] = env('ADMIN_ROUTE_DOMAIN') . '/api/payment/llpay_notify';
        $params['match_success_url'] = env('SITE_DOMAIN').'/#/order/list';
        $params['support_match'] = 0;
        $params['support_refund'] = 1;
        $params['service_type'] = 'CREATE';
        $params['txn_type'] = 'GENERAL_CONSUME';
        $params['user_id'] = $order['member_id'];
        $params['user_type'] = 'ANONYMOUS';
        $params['notify_url'] = env('ADMIN_ROUTE_DOMAIN') . '/api/payment/llpay_notify';

        $Offlinetrans = [];
        // $Offlinetrans['txn_seqno'] = $order['order_bn'].'_'.date('YmdHis');
        // $Offlinetrans['txn_seqno'] = $order['order_bn']. rand(1000, 9999);
        $Offlinetrans['txn_seqno'] = $order['order_bn'];
        $Offlinetrans['txn_time'] = $current;
        $Offlinetrans['total_amount'] = $order['amount'];
        $Offlinetrans['order_info'] = json_encode(['order_bn'=>$order['order_bn'], 'payment_type'=>'lianlpay_offline']);
        $Offlinetrans['goods_name'] = implode(';', $goods_name);
        $params['Offlinetrans'] = $Offlinetrans;

        $PayeeInfo = [];
        $PayeeInfo['payee_id'] = env('OID_PARTNER');
        $PayeeInfo['payee_type'] = 'MERCHANT';
        $PayeeInfo['payee_amount'] = $order['amount'];
        $PayeeInfo['payee_accttype'] = 'MCHOWN';
        $params['PayeeInfo'] = $PayeeInfo;

        $VirtualInfo = [];
        $VirtualInfo['apply_username'] = $order['member']['username'];
        $VirtualInfo['apply_userid'] = $order['member_id'];
        $VirtualInfo['apply_sence'] = 'consume';
        $VirtualInfo['bboc_notify'] = env('ADMIN_ROUTE_DOMAIN') . '/api/payment/llpay_notify';
        $params['VirtualInfo'] = $VirtualInfo;

        // 测试环境地址
        // $url = 'https://accpgw-ste.lianlianpay-inc.com/v1/lzb/init-lzb';
        $url = 'https://accpgw.lianlianpay.com/v1/lzb/init-lzb';
        // dd('sss', $params, $this->merchant_private_key_path);
        $result = self::sendRequest($url, json_encode($params));
        // dd('aa', $result);

        $res = json_decode($result, 1);
        return $res;
    }

    // 获取支付链接
    public function getPayLink($order, $res_)
    {
        $datetime = date('YmdHis');

        $params = [
            'timestamp' => $datetime,
            'oid_partner' => env('OID_PARTNER'),
            'match_success_url' => env('ADMIN_ROUTE_DOMAIN') . '/api/payment/llpay_notify',
            'service_type' => 'QUERY',
            'Offlinetrans' => [
                'txn_seqno' => $res_['txn_seqno'],
            ],
        ];

        // 测试环境地址
        // $url = 'https://accpgw-ste.lianlianpay-inc.com/v1/lzb/init-lzb';
        // 正式
        $url = 'https://accpgw.lianlianpay.com/v1/lzb/init-lzb';
        // dd('sss', $params);
        $result = self::sendRequest($url, json_encode($params));
        // dd('aa', $result);

        $res = json_decode($result, 1);
        // dd($res_, $params, $res);
        return $res;
    }

    public static function sendRequest($url, $content): StreamInterface
    {
        if (empty($url)) {
            Logger()->error("[发送请求中]：请求URL非法，请核实！");
        }
        if (empty($content)) {
            Logger()->error("[发送请求中]：请求参数非法，请核实！");
        }

        $signVar = self::sign($content);

        Logger()->info("[发送请求中]：请求URL为：" . $url);
        Logger()->info("[发送请求中]：请求参数为：: " . $content);
        Logger()->info("[发送请求中]：请求签名值为：: " . $signVar);

        $client = new Client();
        // 拼接请求头
        $header = [
            'timestamp' => date("YmdHis"),
            'Signature-Data' => $signVar,
            'Signature-Type' => 'RSA',
            'Content-Type' => 'application/json;charset=UTF-8',
        ];
        Logger()->info("[请求中]：请求头为：" . json_encode($header));
        // 发送请求
        $response = $client->post($url,
            [
                'body' => $content,
                'headers' => $header,
                'verify' => false
            ]);

        Logger()->info("[请求完成]：响应结果为：" . $response->getBody());

        foreach ($response->getHeaders() as $name => $values)
        {
            foreach ($values as $value) {
                if ($name == 'Signature-Data')
                {
                    Logger()->info("[请求完成]：响应签名为：" . $value);
                    // 验签
                    $isValid = self::checkSign($response->getBody(), $value);
                    if ($isValid == 1)
                    {
                        Logger()->info("[请求完成]：验签结果为：正确");
                    }
                    else
                    {
                        Logger()->info("[请求完成]：验签结果为：错误");
                    }
                }
            }
        }

        // 输出完整的请求报文（仅用于调试）
        // dd($url, $header, json_decode($content,1), json_decode($response->getBody(),1));
        return $response->getBody();
    }

    /**
     * 对数据先进行MD5处理，后使用私钥进行RSA加签
     * @param $data 待加签数据
     * @return string 返回签名
     */
    public static function sign($data = '')
    {
        if (empty($data)) {
            Logger()->error("[加签处理中]：待加签数据为空，请核实！");
            return False;
        }

        $merchant_private_key_path = '/Payment/Lianlianpay/keys/rsa_private_key.pem';
        $private_key = self::_get_pem_content($merchant_private_key_path);
        if (empty($private_key)) {
            Logger()->error("[加签处理中]：私钥错误，请核实！");
            return False;
        }

        $pkeyid = openssl_get_privatekey($private_key);
        if (empty($pkeyid)) {
            Logger()->error("[加签处理中]：私钥错误，请核实！");
            return False;
        }

        // 对数据进行MD5处理
        $md5Var = md5($data);
        Logger()->info("[加签处理中]：待签名源内容：" . $data);
        Logger()->info("[加签处理中]：待签名源内容对应MD5值为：" . $md5Var);

        // 使用数据的MD5值和私钥进行RSA加密
        $verify = openssl_sign($md5Var, $signature, $pkeyid, OPENSSL_ALGO_MD5);
        $result = base64_encode($signature);
        // dd($verify, $md5Var, $signature, $pkeyid , $result);
        Logger()->info("[加签处理中]，签名值为：" . $result);
        return $result;
    }

    /**
     * 利用连连公钥和进行验签
     * @param $data 待验证数据
     * @param $signature 签名值
     * @return -1:error验签异常 1:correct验证成功 0:incorrect验证失败
     */
    public static function checkSign($data = '', $signature = '')
    {
        if (empty($data) || empty($signature)) {
            Logger()->error("[验签处理中]：待验签数据或签名值为空，请核实！");
            return False;
        }

        $merchant_public_key_path = '/Payment/Lianlianpay/keys/rsa_public_key.pem';
        $public_key = self::_get_pem_content($merchant_public_key_path);
        if (empty($public_key)) {
            Logger()->error("[验签处理中]：验签公钥错误，请核实！");
            return False;
        }
        Logger()->info("[验签处理中]：待验签public_key：" . $public_key);

        $pkeyid = openssl_get_publickey($public_key);

        if (empty($pkeyid)) {
            Logger()->error("[验签处理中]：验签公钥错误，请核实！");
            return False;
        }

        // $data = urldecode($data);
        // 对数据进行MD5处理
        $md5Var = md5($data);
        Logger()->info("[验签处理中]：待验签pkeyid：" . $pkeyid);
        Logger()->info("[验签处理中]：待验签数据为：" . $data);
        Logger()->info("[验签处理中]：待验签数据对应MD5值为：" . $md5Var);
        Logger()->info("[验签处理中]：待验签名值为：" . $signature);

        // dd($md5Var, $signature, base64_decode($signature), OPENSSL_ALGO_MD5);

        $signature = base64_decode($signature);
         // Logger()->info("[验签处理中]：待验签名值decode后为：" . $signature);
        // 使用数据的MD5值和签名值进行RSA校验
        $ret = openssl_verify($md5Var, $signature, $pkeyid, OPENSSL_ALGO_MD5);
        switch ($ret) {
            case 0:
                Logger()->info("[验签处理中]：验签完成，验签结果为：错误");
                break;
            case 1:
                Logger()->info("[验签处理中]：验签完成，验签结果为：正确");
                break;
            default:
                Logger()->error("[验签处理中]：验签异常！");
                break;
        }
        // dd($md5Var, $signature, $pkeyid, OPENSSL_ALGO_MD5,$ret);
        return $ret;
    }

    private static function _get_pem_content($file_path)
    {
        return file_get_contents(dirname(__FILE__, 3) . $file_path);
    }

}

?>